package com.gmrz.uaf.remote.protocol.v1.processor;

import com.gmrz.service.challengestore.ChallengeStoreException;
import com.gmrz.uaf.common.*;
import com.gmrz.uaf.crypto.CertUtil;
import com.gmrz.uaf.crypto.CryptoConstants;
import com.gmrz.uaf.crypto.KeyAttestation.KeyAttestationBcUtil;
import com.gmrz.uaf.crypto.sm.SM2Utils;
import com.gmrz.uaf.crypto.spi.UAFAuthAlgorithmSuite;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.db.UAFDAOFactory;
import com.gmrz.uaf.db.dao.AuthenticatorDAO;
import com.gmrz.uaf.db.dao.UasConfigDAO;
import com.gmrz.uaf.policy.verification.PolicyVerificationException;
import com.gmrz.uaf.protocol.v1.json.UAFSchemaBuilder;
import com.gmrz.uaf.protocol.v1.processor.UAFRegFinishProcessor;
import com.gmrz.uaf.protocol.v1.processor.exception.*;
import com.gmrz.uaf.protocol.v1.schema.*;
import com.gmrz.uaf.protocol.v1.validaton.ValidationException;
import com.gmrz.uaf.protocol.v1.validaton.reg.RegAssertionValidator;
import com.gmrz.uaf.remote.plugin.AuthServicePluginManager;
import com.gmrz.uaf.server.ServerContext;
import com.gmrz.uaf.server.validation.MaxRegValidator;
import com.gmrz.uas.plugin.caservice.CertServicePlugin;
import com.gmrz.uas.plugin.caservice.bean.CertBean;
import com.gmrz.uas.plugin.caservice.bean.P10Bean;
import com.gmrz.uas.plugin.exception.PluginException;
import com.gmrz.util.Convert;
import com.gmrz.util.CryUtil;
import com.gmrz.util.Strings;
import com.gmrz.util.UUIDGenerator;
import com.google.gson.*;
import com.google.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.*;

public class UAPCertRegFinishProcessor extends UAFRegFinishProcessor {
    private static final Logger LOG = LogManager.getLogger(UAPCertRegFinishProcessor.class);

    AuthServicePluginManager serviceManager;

    @Inject
    public void setServiceManager(AuthServicePluginManager serviceManager) {
        this.serviceManager = serviceManager;
    }


    @Override
    protected Authenticator processAssertion(RegistrationResponse registrationResponse,
                                             RegistrationAssertion registrationAssertion, AuthenticatorSpec spec,
                                             String hashedUserName, String custNo, String appID,
                                             String deviceID, String authType, String transType,String ext,String from) throws ValidationException,
            DAOException, PolicyVerificationException, MetadataNotFoundException, TLVParsingException,
            CertificateValidationException, SignatureValidationException, IOException, ChallengeStoreException, PluginException, CertificateException, SignatureException, NoSuchAlgorithmException {
        LOG.info("Validating Registration Assertion.");
        spec.setAuthType(authType);
        RegAssertionValidator validator = new RegAssertionValidator(spec, registrationResponse.getFCPBase64());
        if (this.getServerConfig().getPerformModel() < Constants.PERFORM_MODEL_LEVEL_3 && !ServerContext.getServerContext().isPerform()) {
            validator.validate(registrationAssertion);
        }
        LOG.info("Validation completed for Registration Assertion.");

        AAID aaid = spec.getAAID();
        LOG.info("AAID[{}]", aaid);
        String assertionScheme = registrationAssertion.getKeyRegistrationDataScheme();
        LOG.info("Assertion Scheme[{}]", assertionScheme);

        ASMRegResponse asmRegResponse = registrationAssertion.getAuthenticatorResponse();
        KeyRegistrationData krd = asmRegResponse.getKRD();
        String krdDeviceID = krd.getDeviceID();
        if(Strings.isNotBlank(krdDeviceID)){
            if(!krdDeviceID.equals(deviceID.substring(2))){
                LOG.error("validate krd deviceID error krdDeviceID:"+krdDeviceID+",deviceID:"+deviceID);
                throw new SignatureValidationException(UAFErrorCode.PROTOCOL_VALIDATE_DEVICEID_FAILED);
            }
        }
        List<Certificate> attestationCerts = asmRegResponse.getAttestationCerts();
        if (this.getServerConfig().getPerformModel() < Constants.PERFORM_MODEL_LEVEL_3 && !ServerContext.getServerContext().isPerform()) {

            if (null != spec.getSupportedExtensions() && !spec.getSupportedExtensions().isEmpty()) {
                boolean issoft = false;
                if (null != registrationAssertion.getExtensions() && !registrationAssertion.getExtensions().isEmpty()) {
                    processAndroidKeyAttestation(spec, registrationAssertion, registrationResponse.getFinalChallengeParams().getServerChallenge().getChallenge(), registrationResponse.getFCPBase64(), issoft);
                }else {
                    String msg = "No Keystore assertions exts found in the response for user["
                            + hashedUserName + "], aaid[" + aaid.format() + "].";
                    logAndThrowRFE(msg);
                }
            }else {

                if (asmRegResponse.getKRD().getSignAlgorithm() == CryptoConstants.UAFAlgSign.UAF_ALG_SIGN_SM2.getSuite()) {
                    // 使用sm算法校验
                    sm(asmRegResponse, spec);
                } else {
                    // 使用rsa算法校验
                    rsa(asmRegResponse, spec);
                }
            }
            boolean dnFlag = validateDN(asmRegResponse.getP10Bytes(), registrationResponse.getOperationHeader().getServerData().getValidateDataHash(Constants.CERT_DN));
            if(!dnFlag){
                throw new SignatureValidationException(UAFErrorCode.PROTOCOL_VALIDATE_P10_DN_HASH_FAILED);
            }
        }
        // 初始化证书插件
        CertServicePlugin certServicePlugin = serviceManager.getCertServicePlugin(authType);
        P10Bean p10Bean = new P10Bean();
        p10Bean.setP10(asmRegResponse.getP10Bytes());
        p10Bean.setAuthCode(asmRegResponse.getKRD().getPreAuthCode());
        p10Bean.setRefCode(asmRegResponse.getKRD().getRefCode());
        p10Bean.setUserName(hashedUserName);
        p10Bean.setExt(ext);
        p10Bean.setAlgorithm(asmRegResponse.getKRD().getSignAlgorithm());
        p10Bean.setDnJson(registrationResponse.getOperationHeader().getServerData().getValidateDataHash(Constants.CERT_DN_JSON));
        p10Bean.setAppID(appID);
        p10Bean.setAuthType(authType);
        p10Bean.setTransType(transType);
        // 生成证书
        CertBean certBean = certServicePlugin.createCert(p10Bean);
        List<Extension> exts = registrationAssertion.getExtensions();
        if ((exts != null) && (!exts.isEmpty())) {
            processExtensions(exts);
        }
        Authenticator retval = new Authenticator().withAAID(aaid).withKeyID(new KeyID().withId(krd.getKeyID()))
                .withKeyFormat(krd.getKeyFormat()).withAuthenticatorVersion(krd.getAuthenticatorVersion());
        retval.setID(UUIDGenerator.generateID());
        retval.setAppID(appID);
        retval.setDeviceID(deviceID);
        retval.setAuthType(authType);
        retval.setTransType(transType);
        retval.setPubKey(Convert.toBase64(krd.getPubKey()));
        retval.setStatus(registrationResponse.getRegistrationStatus().getStatus());
        retval.setCustNo(custNo);
        retval.setUserName(hashedUserName);
        retval.setMetadata(spec);
        retval.setRegCounter(krd.getRegisterCounter());
        retval.setSignCounter(krd.getSignCounter());
        retval.setFrom(from);
        retval.setFidoPubkey(Convert.toBase64(krd.getFidoPubkey()));
        List<PNGCharacteristics> firstPngCharacteristics = new ArrayList<PNGCharacteristics>();
        List<PNGCharacteristics> pngCharacteristics = registrationAssertion.getTcDisplayPNGCharacteristics();
        if (null != pngCharacteristics && pngCharacteristics.size() > 0) {
            firstPngCharacteristics.add(pngCharacteristics.get(0));
        }
        retval.setPngCharacteristics(firstPngCharacteristics);
        byte[] uviBytes = krd.getUvi();
        if ((uviBytes != null) && (uviBytes.length > 0)) {
            retval.setUvi(Convert.toBase64(uviBytes));
            List<String> base64UvsList = new ArrayList<String>();
            base64UvsList.add(Convert.toBase64(uviBytes));
            retval.setUvs(base64UvsList);
        }
        if (null != certBean) {
            CertificateBean certifiCateBean = new CertificateBean();
            certifiCateBean.setAuthId(retval.getID());
            certifiCateBean.setCertId(certBean.getCertId());
            certifiCateBean.setCertText(certBean.getCertText());
            certifiCateBean.setCreates(new Timestamp(System.currentTimeMillis()));
            certifiCateBean.setCrtId(UUIDGenerator.generateID());
            certifiCateBean.setKeyId(retval.getKeyID().toString());
            certifiCateBean.setRegCounter(0);

            certifiCateBean.setAaid(retval.getAAID().format());
            certifiCateBean.setAuthType(retval.getAuthType());
            certifiCateBean.setDeviceId(retval.getDeviceID());
            certifiCateBean.setTenantId(retval.getAppID());
            certifiCateBean.setTransType(retval.getTransType());
            retval.setCertificateBean(certifiCateBean);

            // 证书发布时间
            Date issuedTime = null;
            // 证书到期时间
            Date expireTime = null;
            // 证书的序列号
            String serialNumber = null;
            if (asmRegResponse.getKRD().getSignAlgorithm() == CryptoConstants.UAFAlgSign.UAF_ALG_SIGN_SM2.getSuite()) {
                //sm
                try {
                    X509Certificate cert = (X509Certificate) SM2Utils.getCert(Convert.fromBase64(certBean.getCertText()));
                    issuedTime = cert.getNotBefore();
                    expireTime = cert.getNotAfter();
                    serialNumber = cert.getSerialNumber().toString();
                } catch (Exception e) {
                    LOG.error("get sm2 certificate from string error", e);
                }
            } else {
                //rsa
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                X509Certificate cert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(Convert.fromBase64(certBean.getCertText())));
                issuedTime = cert.getNotBefore();
                expireTime = cert.getNotAfter();
                serialNumber = cert.getSerialNumber().toString();
            }
            CertificateExtendBean certificateExtendBean = new CertificateExtendBean();
            certificateExtendBean.setCertExtId(UUIDGenerator.generateID());
            certificateExtendBean.setAuthId(retval.getID());
            certificateExtendBean.setCertDN(new String(SM2Utils.getDN(asmRegResponse.getP10Bytes())));
            certificateExtendBean.setIssuedTime(issuedTime);
            certificateExtendBean.setExpireTime(expireTime);
            certificateExtendBean.setCertStatus(Constants.CERT_STATUS_1);
            certificateExtendBean.setCertType("1");
            certificateExtendBean.setSerialNumber(serialNumber);
            retval.setCertificateExtendBean(certificateExtendBean);

        }
        return retval;
    }
    private boolean validateDN(byte[] p10Data, String dnHashFromSaverData) {
        try {
            byte[] data = SM2Utils.getDN(p10Data);
            String dnHash = Convert.toBase64(CryUtil.getSHA256(data));
            if (dnHash.equals(dnHashFromSaverData)) {
                return true;
            }else {
                LOG.error("validateDN error,dn:"+new String(data)+",dn hash:"+dnHash+",");
            }
        } catch (Exception e) {
            LOG.error("validateDN error", e);
        }
        return false;

    }
    private void rsa(ASMRegResponse asmRegResponse, AuthenticatorSpec spec) throws  CertificateValidationException, IOException, SignatureValidationException {
        byte[] rootCert = Convert.fromBase64(spec.getAttestationRootCertificatesStr());
        Certificate deviceCertificate = null;
        boolean signFlag = false;
        try {
            Certificate certificate = cryptoEngine.getAttestationCertHandler().decode(rootCert);
            PublicKey pubKey = certificate.getPublicKey();
            certificate.verify(pubKey);

            byte[] deviceCerts = asmRegResponse.getDevieceCert();
            deviceCertificate = cryptoEngine.getAttestationCertHandler().decode(deviceCerts);
            boolean secondLevelCertValidate = cryptoEngine.getAttestationCertHandler().validateCertificate(Arrays.asList(deviceCertificate), Arrays.asList(certificate), spec.getAAID().format());
            if (!secondLevelCertValidate) {
                throw new CertificateValidationException(UAFErrorCode.VALIDATE_CACERTIFICATE_FAILED);
            }

            byte[] signature = asmRegResponse.getSignature();
            byte[] krdBytes = asmRegResponse.getKRD().getReceivedKRDBytes();
            TLVType2 TLVType2 = new TLVType2() {};
            TLVType2.addTLVSegment((short) 21762, krdBytes);
            byte[] signedKRDBytes = TLVType2.getTLVBytes();

            byte[] signedKRDBytesP1 = new byte[51];
            CryUtil.getRsaPkcs1PaddingDta(CryUtil.getSHA256(signedKRDBytes),signedKRDBytesP1 ,(byte) 1);
            UAFAuthAlgorithmSuite uafAuthAlgorithmSuite = cryptoEngine.getAuthAlgorithmSuite(asmRegResponse.getKRD().getSignAlgorithm());
            signFlag = uafAuthAlgorithmSuite.verifySignature(deviceCertificate.getPublicKey(), signedKRDBytesP1, signature);
        }catch (Exception e){
            LOG.error("rsa error",e);
            throw new CertificateValidationException(UAFErrorCode.VALIDATE_CACERTIFICATE_FAILED);
        }
        if (!signFlag) {
            throw new SignatureValidationException(UAFErrorCode.PROTOCOL_SIGNATURE_VALIDATION_FAILED);
        }
    }

    private void sm(ASMRegResponse asmRegResponse ,AuthenticatorSpec spec ) throws CertificateValidationException, IOException, SignatureValidationException {
        //验证根证书和二级证书
        byte[] rootCert = Convert.fromBase64(spec.getAttestationRootCertificatesStr());
        boolean rootValidate = CertUtil.validateRootCertificate(rootCert);
        if (!rootValidate) {
            throw new CertificateValidationException(UAFErrorCode.VALIDATE_CACERTIFICATE_FAILED);
        }
        //从报文中获取二级证书验证
        byte[] deviceCerts = asmRegResponse.getDevieceCert();
        boolean secondLevelCertValidate = CertUtil.validateSecondLevelCertificate(deviceCerts, rootCert);
        if (!secondLevelCertValidate) {
            throw new CertificateValidationException(UAFErrorCode.VALIDATE_CACERTIFICATE_FAILED);
        }

        byte[] signature = asmRegResponse.getSignature();

        byte[] krdBytes = asmRegResponse.getKRD().getReceivedKRDBytes();
        TLVType2 TLVType2 = new TLVType2() {
        };
        TLVType2.addTLVSegment((short) 21762, krdBytes);
        byte[] signedKRDBytes = TLVType2.getTLVBytes();

        byte[] publicKey = CertUtil.getPublicKey(deviceCerts);
        String pubStr = Convert.toBase64(publicKey);
        boolean signFlag = SM2Utils.verifySignRaw(publicKey, signedKRDBytes, signature);
        if (!signFlag) {
            throw new SignatureValidationException(UAFErrorCode.PROTOCOL_SIGNATURE_VALIDATION_FAILED);
        }
    }
    @Override
    protected Authenticator saveAuthenticator(Authenticator a, AuthenticatorSpec spec, String opType,
                                              Connection conn) throws DAOException , ChallengeStoreException, UAFException{
        Authenticator authenticator = super.saveAuthenticator(a, spec, opType, conn);
        return authenticator;
    }

    @Override
    protected void saveCertBean(Authenticator a,Connection conn) throws DAOException, ChallengeStoreException,UAFException,SQLException {
        CertificateBean certificateBean = a.getCertificateBean();

            AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
            String certCountKey = "uaf.cert.auth.info";
            String certCountValue = this.getmUafChallengeStoreService().getRedisValue(certCountKey);
            if (Strings.isNullOrEmpty(certCountValue)) {
                UasConfigDAO uasConfigDAO = UAFDAOFactory.createUasConfigDAO(conn);
                certCountValue = uasConfigDAO.getUserConfig(Constants.MAX_CERT_NUM).get(0);
                if(Strings.isNullOrEmpty(certCountValue)){
                    throw new UAFCommonRuntimeException(UAFErrorCode.PROTOCOL_NOT_FOUND_CERT_AUTHORIZATION_CODE_FAILED);
                }
                this.getmUafChallengeStoreService().putString(certCountKey, certCountValue);
            }
            if(Strings.isNullOrEmpty(certCountValue)){
                throw new UAFCommonRuntimeException(UAFErrorCode.PROTOCOL_NOT_FOUND_CERT_AUTHORIZATION_CODE_FAILED);
            }

            String certValue = null;
            String securityLevel = null;
            if (CryUtil.verifyData(certCountValue)) {
                byte[] base64 = org.apache.commons.codec.binary.Base64.decodeBase64(certCountValue);
                String certJson = new String(base64);
                JsonObject jsonObject = new JsonParser().parse(certJson).getAsJsonObject();
                JsonObject info = jsonObject.getAsJsonObject("info");
                if (Constants.UAF_CERT_AUTH_CUSTOMER_NAME_VALUE.equals(info.get("c").getAsString())){
                    // 根据aaid查询安全等级
                    securityLevel = Strings.codeConversion(a.getMetadata().getSecureLevel().intValue());
                    if("TEE".equals(securityLevel)){
                        certValue = info.get("t").getAsString();
                    }
                    else if("SE".equals(securityLevel)){
                        certValue = info.get("s").getAsString();
                    }
                    else if("KEYSTORE".equals(securityLevel)){
                        certValue = info.get("k").getAsString();
                    }
                    else {
                        throw new UAFCommonRuntimeException(UAFErrorCode.PROTOCOL_NOT_FOUND_CERT_AUTHORIZATION_CODE_FAILED);
                    }
                }else {
                    throw new UAFCommonRuntimeException(UAFErrorCode.PROTOCOL_NOT_FOUND_CERT_AUTHORIZATION_CODE_FAILED);
                }
            }else {
                throw new UAFCommonRuntimeException(UAFErrorCode.PROTOCOL_NOT_FOUND_CERT_AUTHORIZATION_CODE_FAILED);
            }

            LOG.debug("securityLevel: "+ securityLevel +"   cert count value:" + certValue);
            int certCount = Integer.parseInt(certValue);
            List<CertificateBean> certificateBeans = adao.findCertByDeviceId(a.getDeviceID(), a.getMetadata().getSecureLevel().intValue());
            if (null != certificateBeans && !certificateBeans.isEmpty()) {
                // RegCounter：证书索引
                certificateBean.setRegCounter(certificateBeans.get(0).getRegCounter());
                // IsRepeat：是否重复，0:不重复/1:重复
                certificateBean.setIsRepeat(Constants.IS_REPEAT_1);
            } else {
                certificateBean.setIsRepeat(Constants.IS_REPEAT_0);
                // 元数据对应的安全等级
                certificateBean.setSecureLevel(a.getMetadata().getSecureLevel().intValue());
            }
            // redis缓存或数据库中获取注册的证书的数量
            long maxRegCountLevel = getMaxCount(securityLevel);

            certificateBean.setRegCounter(maxRegCountLevel);
            // 判断已注册的数量是否超过了授权的数量
            if (maxRegCountLevel >= certCount) {
                throw new CertificateValidationException(UAFErrorCode.CERT_COUNT_FAILED);
            }

            // 保存注册的证书信息、渠道等信息
            adao.insertCertificate(certificateBean);
            // 更新证书更新记录信息；证书有到期时间，到期后需要更新证书，并将更新操作存档记录
            int n = adao.updateCertUpdateRecord(a.getUserName(),a.getDeviceID(),a.getAuthType(),a.getTransType(),a.getID());
            if(n > 0){
                // 如果之前已经注册过了证书，只是已经过期了，那直接将新注册申请的证书状态置为激活
                a.getCertificateExtendBean().setCertStatus(Constants.CERT_STATUS_3);
            }
            // 插入证书的扩展信息
            adao.insertCertificateExtend(a.getCertificateExtendBean());
            if(n > 0){
                adao.updateCertExtend(a.getAppID(),a.getID(),"");
            }
    }

    @Override
    protected void updateCertNumCache(Authenticator authenticator) throws ChallengeStoreException {
        if(Constants.IS_REPEAT_0.equals(authenticator.getCertificateBean().getIsRepeat())) {
            //this.getmUafChallengeStoreService().updateCertNumCache();
            String key = Constants.CERT_NUM + "." +Strings.codeConversion(authenticator.getMetadata().getSecureLevel().intValue());
            this.getmUafChallengeStoreService().incr(key);
        }
    }

    @Override
    protected UAFRegFinishResultBean getResultBean(DeregisterationRequest deregisterationRequest, List<Authenticator> successAuthenticators) {
        UAFRegFinishResultBean uafRegFinishResultBean = new UAFRegFinishResultBean();
        uafRegFinishResultBean.setDeregisterationRequest(deregisterationRequest);
        Authenticator authenticator = successAuthenticators.get(0);
        UpdateAuthRequest request = GuiceUtil.getProcessorInjector().getInstance(UpdateAuthRequest.class);
        byte[] challenge = getRandom(32);
        request.getOperationHeader().setApplicationID(this.getServerConfig().getApplicationID(authenticator.getAppID(),authenticator.getTransType()));
        request.setServerChallenge(new ServerChallenge().withChallenge(challenge));
        Policy p = new Policy();
        MatchCriteria matchCriteria = new MatchCriteria();
        matchCriteria.setKeyIDList(Collections.singletonList(authenticator.getKeyID()));
        matchCriteria.setAAID(Collections.singletonList(authenticator.getAAID()));
        p.setAccepted(Collections.singletonList(Collections.singletonList(matchCriteria)));
        request.setPolicy(p);
        List<Extension> extensions = request.getOperationHeader().getExtensions();
        if(null == extensions){
            extensions = new ArrayList<Extension>();
            request.getOperationHeader().setExtensions(extensions);
        }
        Gson gson = UAFSchemaBuilder.getGson();
        byte[] key = new byte[16];
        byte[] byte256 = CryUtil.getSHA256(challenge);
        System.arraycopy(byte256,0,key,0,16);
        byte[] certNoBytes = CryUtil.Aes256Encode(String.valueOf(authenticator.getCertificateBean().getRegCounter()).getBytes(),key);
        LOG.debug("certNoBytes:"+Convert.toBase64(certNoBytes));
        ExtBean extBean = new ExtBean();
        extBean.setCertNo(Convert.toBase64(certNoBytes));
        extBean.setCertPem(authenticator.getCertificateBean().getCertText());
        Extension extension = new Extension();
        extension.setId("cert");
        extension.setData(Convert.toBase64(gson.toJson(extBean).getBytes()));
        extensions.add(extension);
        LOG.debug("extensions ok");
        uafRegFinishResultBean.setUpdateAuthRequest(request);
        uafRegFinishResultBean.setSuccessAuthenticators(successAuthenticators);
        return uafRegFinishResultBean;
    }
    @Override
    protected void verifyMaxAuthenticatorsLimit(int toBeRegisteredAuthenticatorsCount, String username, String tenantid, Connection conn,String authType)
            throws DAOException {
        if (com.google.common.base.Strings.isNullOrEmpty(username)) {
            LOG.error("username can not be empty for registration operation.");
            throw new RegistrationFailedException("username can not be empty for registration operation.");
        }
        AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
        int regAuthenticatorCount = adao.getActiveRegCount(username, tenantid);
        // 获取单用户可注册设备的上线，此处是在程序启动时进行数据库查询并通过guice注入给了MaxRegAuthenticatorsCount属性
        int registeredAuthenticatorsLimit = getServerConfig().getMaxRegAuthenticatorsCount();

        MaxRegValidator maxRegValidator = new MaxRegValidator();
        maxRegValidator.validateAuthenticatorsCount(regAuthenticatorCount, toBeRegisteredAuthenticatorsCount,
                registeredAuthenticatorsLimit, username);
        int certDeviceMaxCount =0;
        String maxNumStr = serviceManager.getPluginConfig(Constants.CERTIFICA).get(Constants.DEVICE_MAX_NUM+"."+tenantid);
        if(Strings.isNotBlank(maxNumStr)){
            certDeviceMaxCount = Integer.parseInt(maxNumStr);
        }
        int activeRegCount = adao.getCertActiveRegCount(username,authType);
        if(activeRegCount >= certDeviceMaxCount){
            throw new UAFCommonRuntimeException(UAFErrorCode.PROTOCOL_REG_MAX_FAILED);
        }
    }


    private void processAndroidKeyAttestation(AuthenticatorSpec spec, RegistrationAssertion regAssertion, byte[] challenge, String fcp, boolean issoft) throws PolicyVerificationException, SignatureValidationException {
        String rootcert = null;
        String ResponseCert = null;
        String certstr = null;

        //元数据获取的证书
        List<Extension> metaext = spec.getSupportedExtensions();
        for (Extension extension : metaext) {
            rootcert = extension.getData();
        }
        JsonParser parser = new JsonParser();
        JsonObject jsonObject = parser.parse(rootcert).getAsJsonObject();
        Certificate AndroidKeyrootCert;
        List<X509Certificate> certList = new ArrayList<X509Certificate>();
        try {
            JsonArray jsonArray = jsonObject.getAsJsonArray("attestationRootCertificates");
            if (null != jsonArray && jsonArray.size() > 0) {
                for (int i = 0; i < jsonArray.size(); i++) {
                    certstr = jsonArray.get(i).getAsString();
                    AndroidKeyrootCert = cryptoEngine.getAttestationCertHandler().decode(Convert.fromBase64(certstr));
                    X509Certificate csCert = (X509Certificate) AndroidKeyrootCert;
                    certList.add(csCert);
                }
            }
        } catch (Exception e) {
            LOG.error("supportedExtensions attestationRootCertificates get cert error");
        }
        X509Certificate[] rootArray = new X509Certificate[certList.size()];
        rootArray = certList.toArray(rootArray);


        //报文获取的证书
        for (Extension regext : regAssertion.getExtensions()) {
            if (!regext.getId().equals("fido.uaf.android.key_attestation")) {
                LOG.error("Extension ID value is invalid");
                throw new PolicyVerificationException(UAFErrorCode.ERROR_KEYSTORE_CERTCHAIN_VALIDATION_FAILED);
            }
            if (regext.getData().equals("p") || regext.getData().equals("a")) {
                LOG.error("Extension Data value is invalid");
                throw new PolicyVerificationException(UAFErrorCode.ERROR_KEYSTORE_CERTCHAIN_VALIDATION_FAILED);
            }
            ResponseCert = regext.getData();
        }
        JsonArray RespJarray = parser.parse(ResponseCert).getAsJsonArray();
        Certificate AndroidKeyRespCert;
        List<X509Certificate> RespcertList = new ArrayList<X509Certificate>();
        try {
            if (null != RespJarray && RespJarray.size() > 0) {
                for (int i = 0; i < RespJarray.size(); i++) {
                    certstr = RespJarray.get(i).getAsString();
                    AndroidKeyRespCert = cryptoEngine.getAttestationCertHandler().decode(Convert.fromBase64(certstr));
                    X509Certificate csCert = (X509Certificate) AndroidKeyRespCert;
                    RespcertList.add(csCert);
                }
            }
        } catch (Exception e) {
            LOG.error("ResponseCert get cert error");
        }
        X509Certificate[] RespcertArray = new X509Certificate[RespcertList.size()];
        RespcertArray = RespcertList.toArray(RespcertArray);

//        byte[] pubkey = regAssertion.getAuthenticatorResponse().getKRD().getPubKey();
        PublicKey pubKey = null;
        byte[] signedKRDBytes = null;
        try {
            PKCS10CertificationRequest p10CSR = new PKCS10CertificationRequest(regAssertion.getAuthenticatorResponse().getP10Bytes());
            byte[] pk = p10CSR.getSubjectPublicKeyInfo().getPublicKeyData().getEncoded();
            pubKey = RespcertArray[0].getPublicKey();
            byte[] pktmp = pubKey.getEncoded();

            regAssertion.getAuthenticatorResponse().getKRD().setPubKey(KeyAttestationBcUtil.decode(pubKey));
            String p1 = Convert.toHex(pk);
            String p2 = Convert.toHex(pktmp);
//            System.out.println("p1:"+p1);
//            System.out.println("p2:"+p2);
            //System.out.println("t_p1:"+KeyAttestationBcUtil.decode(p10CSR.getSubjectPublicKeyInfo().));
//            System.out.println("t_p2:"+Convert.toHex(KeyAttestationBcUtil.decode(pubKey)));
            if(!p2.contains(p1)){
                logAndThrowRFE("verify failed by pubkey");
            }
            byte[] krdBytes = regAssertion.getAuthenticatorResponse().getKRD().getReceivedKRDBytes();
            TLVType2 TLVType2 = new TLVType2() {
            };
            TLVType2.addTLVSegment((short) 21762, krdBytes);
            signedKRDBytes = TLVType2.getTLVBytes();
        }catch (IOException e){
            logAndThrowRFE("Failed to get krd bytes");
        }
        boolean verifyvalue = KeyAttestationBcUtil.verifyRSA(RespcertArray,rootArray,signedKRDBytes,pubKey,issoft);
        if (!verifyvalue) {
            LOG.error("Failed to Certificate chain validation is missing");
            throw new PolicyVerificationException(UAFErrorCode.ERROR_KEYSTORE_CERTCHAIN_VALIDATION_FAILED);
        }

    }


}
